﻿using System;
using System.Collections.Generic;
using PathFind.Algo;
using UnityEngine;
using UnityEngine.UI;

namespace PathFind {
    public partial class SquireGrid : MonoBehaviour {
        public static float SquireLength = 1f;
        public static float PosOffset = SquireLength / 2;

        [SerializeField]
        Mesh mesh = default;

        [SerializeField]
        Material material = default;

        public int Width = 30, Height = 30;

        static int positionsId = Shader.PropertyToID("_TransMatrices");
        static int baseColorId = Shader.PropertyToID("_BaseColor");

        private int _GridCount;
        ComputeBuffer transBuffer, baseColorBuffer;
        Color[] baseColors;
        Matrix4x4[] transMatrices;
        MaterialPropertyBlock propertyBlock;
        Bounds bounds;
        bool isSizeChange;
        bool isColorChange;
        private BoxCollider _boxCollider;

        // void Awake() {
        //     OnValidate();
        // }
        //
        // private void OnValidate() {
        //     if (transBuffer != null && enabled) {
        //         DestroyMap();
        //         CreateMap();
        //     }
        // }

        private void OnDisable() {
            DestroyMap();
        }

        private void DestroyMap() {
            transBuffer?.Release();
            baseColorBuffer?.Release();
            transBuffer = null;
            baseColorBuffer = null;
        }

        public void RefreshMap(int width, int height) {
            Width = width;
            Height = height;
            DestroyMap();
            CreateMap();
        }

        void CreateMap() {
            isSizeChange = true;
            isColorChange = true;
            _GridCount = Width * Height;
            transMatrices = new Matrix4x4[_GridCount];
            baseColors = new Color[_GridCount];
            // 单个元素的单位是 byte
            transBuffer = new ComputeBuffer(_GridCount, 4 * 4 * 4);
            baseColorBuffer = new ComputeBuffer(_GridCount, 4 * 4);

            if (propertyBlock == null) {
                propertyBlock = new MaterialPropertyBlock();
            }

            propertyBlock.SetBuffer(positionsId, transBuffer);
            propertyBlock.SetBuffer(baseColorId, baseColorBuffer);
            if (_boxCollider == null) {
                _boxCollider = gameObject.GetComponent<BoxCollider>();
                if (_boxCollider == null) {
                    _boxCollider = gameObject.AddComponent<BoxCollider>();
                }
            }

            _boxCollider.center = GetCenterPos();
            _boxCollider.size = GetGridSize();
            ReleaseAllLabel();
        }

        public static int CellIndex(int x, int y, int _Width) {
            return y * _Width + x;
        }

        public int CellIndex(int x, int y) {
            return CellIndex(x, y, Width);
        }

        public int CellIndex(Coordinate co) {
            return CellIndex(co.x, co.y);
        }

#if SHOW_GRID_TEXT
        [SerializeField]
        private GameObject TextCanvas;

        [SerializeField]
        public GameObject _DisText;

        private readonly Dictionary<int, Tuple<Text, GameObject>> _textMap = new Dictionary<int, Tuple<Text, GameObject>>();

        public void ChangeLabel(Coordinate co, float totalScore) {
            var scoreStr = totalScore.ToString("0.0");
            var idx = CellIndex(co);
            if (_textMap.ContainsKey(idx)) {
                var tup = _textMap[idx];
                var textField = tup.Item1;
                textField.text = scoreStr;
                return;
            }
            var gameObj = Instantiate(_DisText, TextCanvas.transform);
            var textField2 = gameObj.GetComponent<Text>();
            textField2.text = scoreStr;
            _textMap[idx] = new Tuple<Text, GameObject>(textField2, gameObj);
            var xPos = co.x * SquireLength + PosOffset;
            var yPos = co.y * SquireLength + PosOffset;
            gameObj.transform.localPosition = new Vector3(xPos, yPos, -0.2f); // TextCanvas 的x旋转是90度
        }

        public void ClearAllLabel() {
            foreach (var ent in _textMap) {
                var textField = ent.Value.Item1;
                textField.text = "";
            }
        }

        partial void ReleaseAllLabel() {
            foreach (var ent in _textMap) {
                Destroy(ent.Value.Item2);
            }
            _textMap.Clear();
        }
#endif
        partial void ReleaseAllLabel();

        void Update() {
            if (transBuffer == null) {
                return;
            }

            if (isSizeChange) {
                isSizeChange = false;
                var scaleVec = Vector3.one * SquireLength;
                var rotation = Quaternion.Euler(90, 0, 0);
                var baseColor = Color.white;
                for (int j = 0; j < Height; j++) {
                    for (int i = 0; i < Width; i++) {
                        var idx = CellIndex(i, j);
                        var pos = new Vector3(i * SquireLength + PosOffset, 0, j * SquireLength + PosOffset);
                        baseColors[idx] = baseColor;
                        var mat = Matrix4x4.TRS(pos, rotation, scaleVec);
                        transMatrices[idx] = mat;
                    }
                }

                transBuffer.SetData(transMatrices);
                bounds = new Bounds(
                    transform.position,
                    new Vector3(Width * SquireLength * 3, Height * SquireLength * 3, Height * SquireLength * 3)
                );
            }

            if (isColorChange) {
                isColorChange = false;
                baseColorBuffer.SetData(baseColors);
            }

            Graphics.DrawMeshInstancedProcedural(mesh, 0, material, bounds, _GridCount, propertyBlock);
        }

        public void ChangeColor(int x, int y, Color color) {
            var idx = CellIndex(x, y);
            baseColors[idx] = color;
            isColorChange = true;
        }

        public void ChangeColor(Coordinate co, Color color) {
            ChangeColor(co.x, co.y, color);
        }

        public void MultiplyColor(int x, int y, Color color) {
            var idx = CellIndex(x, y);
            var oldColor = baseColors[idx];
            baseColors[idx] = oldColor * color;
            isColorChange = true;
        }

        public Coordinate GetCoordinate(Vector3 hitPoint) {
            short x = (short) (hitPoint.x / SquireLength);
            short y = (short) (hitPoint.z / SquireLength);
            return new Coordinate(x, y);
        }

        public Vector3 GetCenterPos() {
            return new Vector3(SquireLength * Width / 2, 0, SquireLength * Height / 2);
        }

        public Vector3 GetGridSize() {
            return new Vector3(SquireLength * Width, 0.1f, SquireLength * Height);
        }
    }
}